import re
from django.db import models
from wechatpy.exceptions import WeChatPayException
from finance_c2c.local_settings import Config
from core.wchat.mini_base import wx_pay, wx_pay_howho
from .WXInstance import KLBWxPay


# Create your models here.

class WxUserInfo(models.Model):
    openid_e = models.CharField(max_length=128, db_index=True, blank=True, null=True, verbose_name="加密openid")
    nick_name = models.CharField(max_length=128, blank=True, null=True)
    openid = models.CharField(max_length=128, db_index=True)
    head_url = models.CharField(max_length=512, blank=True, null=True)
    sex = models.IntegerField(blank=True, null=True)
    country = models.CharField(max_length=128, blank=True, null=True)
    province = models.CharField(max_length=128, blank=True, null=True)
    city = models.CharField(max_length=128, blank=True, null=True)
    unionid = models.CharField(max_length=128, blank=True, null=True)
    session_key = models.CharField(max_length=128, db_index=True, blank=True, null=True)
    create_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = '微信用户'
        verbose_name_plural = verbose_name
        db_table = 'wx_user_info'

    def update_info(self, user_info):
        """
        更新用户信息
        :param user_info:
        :return:
        """
        self.nick_name = re.sub(u'[^0-9A-Za-z\u4e00-\u9fa5]', u"", user_info.get("nickname"))
        self.head_url = user_info.get("headimgurl")
        self.sex = user_info.get("sex")
        self.country = user_info.get("country")
        self.province = user_info.get("province")
        self.city = user_info.get("city")
        self.unionid = user_info.get("unionid")
        self.save()

    def __str__(self):
        if self.nick_name:
            return self.nick_name
        else:
            return ""


class MiniWxUserInfo(models.Model):
    openid_e = models.CharField(max_length=128, db_index=True, blank=True, null=True, verbose_name="加密openid")
    nick_name = models.CharField(max_length=128, blank=True, null=True)
    openid = models.CharField(max_length=128, db_index=True)
    head_url = models.CharField(max_length=512, blank=True, null=True)
    sex = models.IntegerField(blank=True, null=True)
    country = models.CharField(max_length=128, blank=True, null=True)
    province = models.CharField(max_length=128, blank=True, null=True)
    city = models.CharField(max_length=128, blank=True, null=True)
    unionid = models.CharField(max_length=128, blank=True, null=True)
    session_key = models.CharField(max_length=128, db_index=True, blank=True, null=True)
    create_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = '微信用户'
        verbose_name_plural = verbose_name
        db_table = 'wx_mini_user_info'

    def update_info(self, user_info):
        """
        更新用户信息
        :param user_info:
        :return:
        """
        self.nick_name = re.sub(u'[^0-9A-Za-z\u4e00-\u9fa5]', u"", user_info.get("nickname"))
        self.head_url = user_info.get("headimgurl")
        self.sex = user_info.get("sex")
        self.country = user_info.get("country")
        self.province = user_info.get("province")
        self.city = user_info.get("city")
        self.unionid = user_info.get("unionid")
        self.save()

    def __str__(self):
        if self.nick_name:
            return self.nick_name
        else:
            return ""


class WxMenu(models.Model):
    """
    微信公众号菜单
    """
    ACCOUNT_TYPE_CHOICES = (
        ("None", '主菜单'),
        ("view", '跳转链接'),
        ("click", '图文消息'),
    )
    name = models.CharField(max_length=32, verbose_name="主菜单名称")
    order = models.IntegerField(verbose_name="排序顺序")
    type = models.CharField(max_length=16,
                            choices=ACCOUNT_TYPE_CHOICES,
                            default="None",
                            verbose_name="按钮类型")
    key_or_url = models.CharField(max_length=256, verbose_name="关键词或者链接", blank=True, null=True)

    @property  # 将方法定义成属性
    def key(self):
        return str("url" if self.type == "view" else "key")

    class Meta:
        verbose_name = "微信公众号主菜单"
        verbose_name_plural = verbose_name
        ordering = ["order"]

    def __unicode__(self):
        return self.name


class WxMenuChild(models.Model):
    """
    微信公众号子菜单
    """
    ACCOUNT_TYPE_CHOICES = (
        ("view", '跳转链接'),
        ("click", '图文消息'),
    )
    name = models.CharField(max_length=7, verbose_name="子菜单名称")
    order = models.IntegerField(verbose_name="排序顺序")
    type = models.CharField(max_length=16,
                            choices=ACCOUNT_TYPE_CHOICES,
                            default="view",
                            verbose_name="按钮类型")
    key_or_url = models.CharField(max_length=256, verbose_name="关键词或者链接")
    wx_menu = models.ForeignKey(WxMenu, verbose_name="添加到主菜单", blank=True, null=True)

    @property  # 将方法定义成属性
    def key(self):
        return str("url" if self.type == "view" else "key")

    class Meta:
        verbose_name = "微信公众号子菜单"  # admin后台显示
        verbose_name_plural = verbose_name  # 复数显示
        ordering = ["order"]

    def __unicode__(self):
        return self.name  # 对象序列化成unicode


class WxPayOrderManager(models.Manager):
    def get_wx_order(self, wx_order_info):
        """
        通过此条件获取数据
        :param wx_order_info:
        :return:
        """
        out_trade_no = wx_order_info.get("out_trade_no")
        appid = wx_order_info.get("appid")
        mch_id = wx_order_info.get("mch_id")
        return self.get(out_trade_no=out_trade_no, appid=appid, mch_id=mch_id)

    def get_order_is_pay(self, out_trade_no, nonce_str):
        """
        获取支付状态
        :param out_trade_no: 商户订单号
        :param nonce_str: 随机串
        :return:
        """
        try:
            return self.get(out_trade_no=out_trade_no, nonce_str=nonce_str).is_pay
        except Exception as e:
            return 0


class WxPayOrder(models.Model):
    model = models.CharField(max_length=16, blank=True, null=True)
    appid = models.CharField(max_length=40, blank=True, null=True)
    bank_type = models.CharField(max_length=32, blank=True, null=True, verbose_name="银行类型")

    mch_id = models.CharField(max_length=40, blank=True, null=True)
    nonce_str = models.CharField(max_length=64, blank=True, null=True, verbose_name="随机串")
    body = models.CharField(max_length=128, blank=True, null=True, verbose_name="描述")
    out_trade_no = models.CharField(max_length=32, blank=True, null=True, verbose_name="商户订单号")
    total_fee = models.IntegerField(blank=True, null=True, verbose_name="支付金额(以分为单位) 微信应付")
    spbill_create_ip = models.CharField(max_length=20, verbose_name="用户ip", blank=True, null=True)
    trade_type = models.CharField(max_length=16, verbose_name="交易类型(h5:MWEB , 公众号支付：JSAPI)")
    openid = models.CharField(max_length=64, blank=True, null=True)
    notify_url = models.CharField(max_length=256, blank=True, null=True, verbose_name="微信支付异步通知地址")
    # < ---------接口返回----------->
    return_code = models.CharField(max_length=64, blank=True, null=True, verbose_name="返回状态码")
    return_msg = models.CharField(max_length=128, blank=True, null=True, verbose_name="返回信息")
    result_code = models.CharField(max_length=16, blank=True, null=True, verbose_name="业务结果")
    err_code_des = models.CharField(max_length=128, blank=True, null=True, verbose_name="错误信息描述")
    prepay_id = models.CharField(max_length=70, verbose_name="预支付交易会话标识", blank=True, null=True)
    mweb_url = models.CharField(max_length=1024, verbose_name="支付跳转url", blank=True, null=True)
    # < ---------通知信息----------->
    # notify_return_code = return_code
    notify_return_code = models.CharField(max_length=64, blank=True, null=True, verbose_name="通知结果code")
    # notify_return_msg = return_msg
    notify_return_msg = models.CharField(max_length=64, blank=True, null=True, verbose_name="通知结果描述")
    # notify_result_code= result_code
    notify_result_code = models.CharField(max_length=64, blank=True, null=True, verbose_name="通知支付")
    # notify_err_code_des = err_code_des
    notify_err_code_des = models.CharField(max_length=64, blank=True, null=True, verbose_name="通知错误返回的信息描述")
    transaction_id = models.CharField(db_index=True, max_length=40, blank=True, null=True,
                                      verbose_name="微信支付订单号(订单查询使用)")
    time_end = models.CharField(max_length=32, blank=True, null=True, verbose_name="支付时间")
    is_pay = models.IntegerField(default=0, db_index=True)
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
    # settlement_total_fee = models.IntegerField(blank=True, null=True, verbose_name="应结金额(以分为单位)",
    #                                            help_text="应结订单金额-订单使用了免充值券才返回，应结订单金额=订单总金额-免充值券金额，即实际结算金额")

    objects = WxPayOrderManager()

    class Meta:
        verbose_name = "微信支付订单"  # admin后台显示
        verbose_name_plural = verbose_name  # 复数显示

    def update_order_notify_data(self, notify_data):
        """
        更新通知数据
        :param notify_data:
        :return:
        """
        return_code = notify_data.get("return_code", None)
        result_code = notify_data.get("result_code", None)
        self.notify_return_code = return_code
        self.notify_return_msg = notify_data.get("return_msg", None)
        self.notify_result_code = result_code
        self.notify_err_code_des = notify_data.get("err_code_des", None)
        self.transaction_id = notify_data.get("transaction_id")
        self.total_fee = notify_data.get("total_fee")
        self.time_end = notify_data.get("time_end")
        self.bank_type = notify_data.get("bank_type")
        self.save()
        if return_code == "SUCCESS" and result_code == "SUCCESS":
            self.is_pay = 1
            self.save()
        return self

    def update_order_return_data(self, return_data):
        print("wechat pay response", return_data)
        self.return_code = return_data.get("return_code", None)
        self.return_msg = return_data.get("return_msg", None)
        self.result_code = return_data.get("result_code", None)
        self.err_code_des = return_data.get("err_code_des", None)
        self.prepay_id = return_data.get("prepay_id", None)
        self.mweb_url = return_data.get("mweb_url", None)
        self.appid = return_data.get("appid", None)
        self.mch_id = return_data.get("mch_id", None)
        self.nonce_str = return_data.get("nonce_str", None)
        self.save()


# 退款记录管理
class RefundRecordManager(models.Manager):
    def create_record_error(self, data, refund_fee, total_fee):
        return_code = data.get("return_code")
        return_msg = data.get("return_msg")
        return self.create(
            return_code=return_code,
            return_msg=return_msg,
            refund_fee=refund_fee,
            total_fee=total_fee
        )

    def create_record(self, data):
        result = dict()
        result["return_code"] = data.get("return_code")
        result["return_msg"] = data.get("return_msg")
        result["result_code"] = data.get("result_code")
        result["err_code"] = data.get("err_code")
        result["err_code_des"] = data.get("err_code_des")
        result["appid"] = data.get("appid")
        result["mch_id"] = data.get("mch_id")
        result["nonce_str"] = data.get("nonce_str")
        result["sign"] = data.get("sign")
        result["out_refund_no"] = data.get("out_refund_no")
        result["refund_id"] = data.get("refund_id")
        result["cash_fee"] = data.get("cash_fee")
        result["total_fee"] = data.get("total_fee")
        result["refund_fee"] = data.get("refund_fee")
        return self.create(**result)

    def wx_refund(self, total_fee, refund_fee, out_refund_no, out_trade_no, notify_url="/shop/api/receive_refund",
                  pay_type="js"):
        """
        :param total_fee:
        :param refund_fee:
        :param out_refund_no:
        :param out_trade_no:
        :param notify_url:
        :param pay_type:
        :return:
        """
        if "http" in notify_url:
            pass
        else:
            notify_url = Config.host + notify_url
        try:
            if pay_type == "mini":
                response = wx_pay.refund.apply(total_fee, refund_fee, out_refund_no, out_trade_no=out_trade_no,
                                               notify_url=notify_url)
            else:
                response = KLBWxPay.refund.apply(total_fee, refund_fee, out_refund_no, out_trade_no=out_trade_no,
                                                 notify_url=notify_url)
        except WeChatPayException as e:
            if e.errmsg:
                print(e.errmsg)
            else:
                print(e.return_msg)
            return False, e.errmsg
        print(response)
        if response.get("result_code") != "SUCCESS":
            RefundRecord.objects.create_record_error(response, refund_fee, total_fee)
            return False, response.get("return_msg")
        self.create_record(response)  # 微信退款记录
        return True, "OK"

    # 完成
    def wx_refund_v2(self, total_fee, refund_fee, out_refund_no, out_trade_no, notify_url="/shop/api/receive_refund",
                     pay_type="js"):
        """
        :param total_fee:
        :param refund_fee:
        :param out_refund_no:
        :param out_trade_no:
        :param notify_url:
        :param pay_type:
        :return:
        """
        if "http" in notify_url:
            pass
        else:
            notify_url = Config.host + notify_url
        try:
            response = wx_pay_howho.refund.apply(total_fee, refund_fee, out_refund_no, out_trade_no=out_trade_no,
                                                 notify_url=notify_url)
        except WeChatPayException as e:
            if e.errmsg:
                print(e.errmsg)
            else:
                print(e.return_msg)
            return False, e.errmsg
        print(response)
        if response.get("result_code") != "SUCCESS":
            RefundRecord.objects.create_record_error(response, refund_fee, total_fee)
            return False, response.get("return_msg")
        self.create_record(response)  # 微信退款记录
        return True, "OK"


# 退款记录
class RefundRecord(models.Model):
    total_fee = models.IntegerField(verbose_name="总价格")
    refund_fee = models.IntegerField(verbose_name="退款金额")
    create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
    update_time = models.DateTimeField(verbose_name="更新时间", auto_now=True)
    return_code = models.CharField(verbose_name="返回状态码", max_length=16, db_index=True)
    return_msg = models.CharField(verbose_name="返回信息", max_length=128)
    # 以下字段在return_code为SUCCESS的时候有返回
    result_code = models.CharField(max_length=16, verbose_name="业务结果", blank=True, null=True)
    err_code = models.CharField(max_length=32, verbose_name="错误代码", blank=True, null=True)
    err_code_des = models.CharField(max_length=128, verbose_name="错误代码描述", blank=True, null=True)
    appid = models.CharField(max_length=32, verbose_name="公众账号ID", blank=True, null=True)
    mch_id = models.CharField(max_length=32, verbose_name="商户号", blank=True, null=True)
    nonce_str = models.CharField(max_length=32, verbose_name="随机字符串", blank=True, null=True)
    sign = models.CharField(max_length=32, verbose_name="签名", blank=True, null=True)
    out_refund_no = models.CharField(max_length=64, verbose_name="商户退款单号", blank=True, null=True)
    refund_id = models.CharField(max_length=32, verbose_name="微信退款单号", blank=True, null=True)
    cash_fee = models.IntegerField(verbose_name="总价格")
    # 通知参数
    notify_return_code = models.CharField(max_length=16, verbose_name="通知返回状态码", help_text="return_code")
    notify_return_msg = models.CharField(max_length=128, verbose_name="通知返回状态码", help_text="return_msg")
    # 以下字段在notify_return_code为SUCCESS的时候有返回：
    refund_status = models.CharField(max_length=16, verbose_name="退款状态")
    settlement_refund_fee = models.IntegerField(verbose_name="退款金额", blank=True, null=True)
    refund_account = models.CharField(verbose_name="退款资金来源", blank=True, null=True, max_length=64)
    objects = RefundRecordManager()

    # 增加退款记录
    class Meta:
        verbose_name = "订单退款"
        verbose_name_plural = verbose_name

    def update_req_info(self, req_info):
        self.refund_status = req_info.get("refund_status")
        self.refund_account = req_info.get("refund_recv_accout")
        self.settlement_refund_fee = req_info.get("settlement_refund_fee")
        self.save()
